<?php

namespace Api\Member\AuthManager\Open;

use JWTAuth;
use JWTFactory;
use Carbon\Carbon;
use Api\RepositoryResourceTrait;

/**
 * 认证仓库
 */
class ManagerRepository
{
    use RepositoryResourceTrait;

    /**
     * 构造方法
     */
    public function __construct(
        \Api\Member\AuthManager\Models\Manager $Model,
        LogRepository                          $LogRepository,
        string $authProviderKey, array $config
    ) {
        $this->Models          = compact('Model');
        $this->LogRepository   = $LogRepository;
        $this->authProviderKey = $authProviderKey;
        $this->config          = $config;
    }

    /**
     * 校验账号与手机是否匹配
     *
     * @return void
     * 
     * @throws \Member\AuthManager\Exceptions\Exception
     */
    public function checkAccountMobile($account, $mobile, $countryCode)
    {
        if (empty($account) || empty($mobile) || empty($countryCode)) {
            throw_exception('Member\AuthManager', 'CheckAccountMobileFail', '输入信息不能为空');
        }
        $count = $this->Model
            ->where('account', $account)
            ->where('mobile', $mobile)
            ->where('country_code', $countryCode)
            ->count();

        if ($count !== 1) {
            throw_exception('Member\AuthManager', 'CheckAccountMobileFail', '账号与手机不匹配');
        }
    }

    /**
     * 重置密码
     *
     * @return void
     * 
     * @throws \Member\AuthManager\Exceptions\Exception
     */
    public function resetPassword($nowPassword, $account, $mobile, $countryCode)
    {
        $manager = $this->Model
            ->where('account', $account)
            ->where('mobile', $mobile)
            ->where('country_code', $countryCode)
            ->firstOrFail();

        $manager->password = $nowPassword;
        $manager->saveOrFail();

        // 记录日志
        $this->LogRepository->makeByManagerId($manager->id, '重置密码');
    }

    /**
     * 登录
     * 
     * @return string $token
     * 
     * @throws \Member\AuthManager\Exceptions\Exception
     */
    public function login($account)
    {
        // 账号校验
        $manager = $this->Model->where('account', $account)->first();
        if (! $manager) {
            throw_exception('Member\AuthManager', 'LoginFail', '账号不存在');
        }

        // 是否禁用
        if ($manager->isForbidden()) {
            throw_exception('Member\AuthManager', 'LoginFail', '账户已被禁用');
        }
        
        // 错误重试间隔判断
        if ($manager->confine_time && Carbon::parse($manager->confine_time)->gt(Carbon::now())) {
            $confineSecond = Carbon::parse($manager->confine_time)->diffInSeconds();
            throw_exception('Member\AuthManager', 'LoginFail', '错误次数超出限制，请于 '.$confineSecond.' 秒后再试');
        }

        // // 密码校验
        // if (! app('hash')->check($password, $manager->password)) {
        //     $manager->auth_fail_num = $manager->auth_fail_num + 1;
        //     if ($manager->auth_fail_num > $this->config['authFailAllowNum']) {
        //         $confineSecond        = ($manager->auth_fail_num - $this->config['authFailAllowNum']) * $this->config['authFailConfineBaseTime'];
        //         $manager->confine_time = Carbon::now()->addSecond($confineSecond);
        //         $manager->save();
        //         throw_exception('Member\AuthManager', 'LoginFail', '错误次数超出限制，请于 '.$confineSecond.' 秒后再试');
        //     }
        //     else {
        //         $manager->save();
        //         throw_exception('Member\AuthManager', 'LoginFail', '密码错误');
        //     }
        // }
        // else {
        //     // 重置错误次数
        //     $manager->auth_fail_num = 0;
        //     $manager->save();
        // }

        // 更新最后一次登录的 IP 地址
        $manager->last_ip = app('request')->ip();
        $manager->save();

        // 记录日志
        $this->LogRepository->makeByManagerId($manager->id, '登录');
        
        // 返回 token
        return $this->getTokenById($manager->id);
    }

    /**
     * 通过 ID 创建 token
     * 
     * @return string $token
     */
    private function getTokenById(int $id)
    {
        // 后期可加随机串存储数据库，提高安全性，可实现单点登录、IP绑定
        $ip      = app('request')->ip();
        $payload = JWTFactory::sub($id)->type($this->authProviderKey)->ip($ip)->make();
        $token   = JWTAuth::encode($payload)->get();
        return $token;
    }

    /**
     * 刷新 token 并将用于换取的 token 列入黑名单
     * 
     * @return string $token
     * 
     * @throws \Tymon\JWTAuth\Exceptions\JWTException
     */
    public function refreshToken()
    {
        $token = JWTAuth::parseToken()->refresh();
        return $token;
    }

    /**
     * 废弃 token 将当前 token 列入黑名单
     * 
     * @return void
     * 
     * @throws \Tymon\JWTAuth\Exceptions\JWTException
     */
    public function logout()
    {
        JWTAuth::parseToken()->invalidate();
    }

    /**
     * 验证 token 并返回账户信息
     * 
     * @return array
     * 
     * @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException      无效 token 解析异常
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException 对应用户不存在
     * @throws \Member\AuthManager\Exceptions\Exception
     */
    public function authenticateByToken()
    {
        $payload = JWTAuth::parseToken()->payload();
        
        // 类型校验
        if ($payload->get('type') !== $this->authProviderKey) {
            throw_exception('Member\AuthManager', 'AuthenticateByTokenFail', '类型校验失败');
        }

        // // ip 校验
        // $ip = app('request')->ip();
        // if ($payload->get('ip') !== $ip) {
        //     throw_exception('Member\AuthManager', 'AuthenticateByTokenFail', 'ip 校验失败');
        // }

        $manager = $this->Model->findOrFail($payload->get('sub'));
        
        // 是否禁用
        if ($manager->isForbidden()) {
            throw_exception('Member\AuthManager', 'AuthenticateByTokenFail', '账户已被禁用');
        }
        
        return [
            'authProviderKey' => $this->authProviderKey,
            'account'         => $manager,
        ];
    }


}
